package com.smh.demo1.a35;

import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.annotation.ImportCandidates;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;

/**
 * @author shiminghui
 * @date 2025/5/9 10:33
 * @description: TODO
 */
public class A35 {
    public static void main(String[] args) {

        // springboot通过@condition注解和延时导入,使得自定义替换默认,未定义走默认的效果.

        GenericApplicationContext context = new GenericApplicationContext();

        // spring容器是允许Bean覆盖的,但是springboot不允许覆盖
        context.setAllowBeanDefinitionOverriding(false);

        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class); // 处理@Configuration注解,是一个beanFactory 后处理器
        context.refresh();


        // 打印Bean
        System.out.println("======================================");
        String[] names = context.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }

        // 测试Bean1是谁创建的
        System.out.println(context.getBean("bean1"));

        context.close();
    }

    @Configuration
    // 怎么一次导入多个配置类呢？(@import),再配合ImportSelector接口可以轻松实现,并且可以从配置文件中获取
    @Import({MyImportSelector.class, MyImportSelector2.class})
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1("自定义");
        }
    }

    // 延时导入,先解析自定义的配置类
    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // 从配置文件读
            List<String> list =
                    SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, this.getClass().getClassLoader());

            return list.toArray(new String[0]);
        }
    }

    static class MyImportSelector2 implements ImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            // 写入这个文件的类不管有没有@Configuration注解,都是会导入,但建议加上
            // AutoConfiguration.class是固定的,它是用来指定文件名的,org.springframework.boot.autoconfigure.AutoConfiguration
            // 这里配置类里面的类太多了,不方便测试,所以我用一个模拟类,这里不知道为啥不生效,还是过滤吧
            ImportCandidates load = ImportCandidates.load(AutoConfiguration.class, this.getClass().getClassLoader());
            ArrayList<String> list = new ArrayList<>();
            load.forEach(e -> {
                if (e.contains("AutoConfiguration3")) {
                    list.add(e);
                }
            });

            System.out.println(list);

            return list.toArray(new String[0]);
        }
    }

    // 模拟第三方配置类
    @Configuration
    static class AutoConfiguration1 {
        @Bean
        @ConditionalOnMissingBean
        public Bean1 bean1() {
            return new Bean1("第三方");
        }


    }

    static class Bean1 {
        private String name;

        public Bean1(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Bean1{" +
                    "name='" + name + '\'' +
                    '}';
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    static class Bean2 {

    }

    static class Bean3 {

    }

    // 模拟第三方配置类
    @Configuration
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }

    }

    // 模拟第三方配置类
    @AutoConfiguration
    static class AutoConfiguration3 {
        @Bean
        public Bean3 bean3() {
            return new Bean3();
        }

    }

    // 用来当文件名的类
    static class MyAutoConfiguration {
    }
}
